module tb_lsu ();
reg                       clk             ;
reg                       rst_n           ;
reg                       pd_rst          ;

always begin
#1;
clk <= ~clk;
end

class lsu_Packet;

    // rand bit                       lsu_req         ;
    rand bit                       lsu_ctrl_unsign ;
    rand bit                       lsu_ctrl_load   ;
    rand bit                       lsu_ctrl_store  ;
    rand bit                       lsu_ctrl_word   ;
    rand bit                       lsu_ctrl_half   ;
    rand bit                       lsu_ctrl_byte   ;
    randc bit [5:0]                lsu_inst_id     ;// Instruction ID
    // rand bit                       lsu_flush       ;
    rand bit [5:0]                 lsu_flush_id    ;
    // rand bit                       retire_vld      ;
    rand bit [31:0]                lsu_op1_val     ;
    rand bit [31:0]                lsu_op2_val     ;
    rand bit [31:0]                lsu_store_data  ;
    rand int                       cycle_remain    ;
    rand bit [4-1:0]               packet_state    ;
    rand bit [9-1:0]               wait_cycle      ;
    int                            packet_id       ;
    constraint c {
         (lsu_ctrl_store & lsu_ctrl_load) ==0; //must ()
         (lsu_ctrl_store | lsu_ctrl_load) ==1;
        cycle_remain >0;
        cycle_remain<50;
        lsu_op2_val+lsu_op1_val >32'h8000_0000;
        lsu_op2_val+lsu_op1_val <=32'hffff_ffff;
        packet_state==0;
        wait_cycle==0;
        
    }
endclass : lsu_Packet

    logic                       lsu_req         ;
    logic                       lsu_ready       ;
    logic                       lsu_ctrl_unsign ;
    logic                       lsu_ctrl_load   ;
    logic                       lsu_ctrl_store  ;
    logic                       lsu_ctrl_word   ;
    logic                       lsu_ctrl_half   ;
    logic                       lsu_ctrl_byte   ;
    logic [5:0]     lsu_inst_id     ;
    logic                       lsu_flush       ;
    logic [5:0]     lsu_flush_id    ;
    logic                       retire_vld      ;
    logic [31:0]                lsu_op1_val     ;
    logic [31:0]                lsu_op2_val     ;
    logic [31:0]                lsu_store_data  ;

    logic [5:0]    lsu_resp_inst_id;
    logic                      lsu_resp_vld    ; 
    logic                      lsu_resp_rd_vld ; 
    logic [5:0]                lsu_resp_rd_ind ; 
    logic [31:0]               lsu_resp_rd_val ; //rd value    
    logic                      lsu_exp_vld     ; //ignore it when exception feature is not implemented
    //DMem Control
    logic                      dmem_req        ;
    logic                      dmem_type       ;
    logic [5:0]    dmem_req_tid    ; // request transaction id , ignore it before we implement cache
    logic [31:0]               dmem_addr       ; 
    logic [31:0]               dmem_wdata      ;
    logic [31:0]               dmem_rdata      ;
    logic                      dmem_ack        ; // Cache will give ACK to LSU if cache hit happen
    logic                      dmem_hit        ;
    logic                      dmem_ack_type   ;
    logic                      dmem_flush      ; 
    //DDR Access Request Interface
    logic                      ddr_req         ;
    logic [5:0]    ddr_req_tid     ; // request transaction id , ignore it before we implement cache
    logic [31:0]               ddr_addr        ; 
    logic                      ddr_ack         ;
    logic                      ddr_ack_type    ;
    logic [5:0]    ddr_ack_tid     ;
    logic                       ddr_type        ;
    logic                       ddr_wdata       ;
    logic                       ddr_rdata       ;
    //Device Access Interface
    //waddr ch
    logic [5:0]    dev_awtid       ;
    logic [31:0]               dev_awaddr      ;
    logic [2:0]                dev_awprot      ;
    logic                      dev_awvalid     ;
    logic                      dev_awready     ;
    //wdata ch 
    logic [5:0]    dev_wtid        ; 
    logic [31:0]               dev_wdata       ;
    logic [3:0]                dev_wstrb       ;
    logic                      dev_wvalid      ;
    logic                      dev_wready      ;
    //b resp ch
    logic [5:0]    dev_btid        ;
    logic  [1:0]               dev_bresp       ;
    logic                      dev_bvalid      ;
    logic                      dev_bready      ;
    //raddr ch
    logic [5:0]    dev_artid       ;
    logic [31:0]               dev_araddr      ;
    logic [2:0]                dev_arprot      ;
    logic                      dev_arvalid     ;
    logic                      dev_arready     ;
    //rdata ch
    logic [5:0]    dev_rtid        ;
    logic  [31:0]              dev_rdata       ;
    logic  [1:0]               dev_rresp       ;
    logic                      dev_rvalid      ;
    logic                      dev_rready      ;

lsu_Packet packet;
lsu_Packet packets[100];
logic cycle_remain_cnt_flag;
logic ddr_req_flag,dmem_req_flag;
logic next_lsu_req_flag;
int send_ptr;
logic test_flag;
function int inst_id_in_lsu (int pac_index);
    inst_id_in_lsu=0;
    for (int i = 0; i < 100; i++) begin
        if (packets[i].lsu_inst_id==packets[pac_index].lsu_inst_id) begin
            if (packets[i].packet_state==2) begin
                inst_id_in_lsu=1;
                break;
            end
        end
    end
endfunction

task flush();
    
endtask : flush

task retitr();
    
endtask : retitr


task send_packet ();
    @(posedge clk);


    next_lsu_req_flag=((lsu_req & lsu_ready)|(!lsu_req));
    if (lsu_req & lsu_ready) begin
        packets[send_ptr].packet_state=2;       //req in lsu
        test_flag=1;
        $display($time,"INFO:packet%d into lsu !",send_ptr);
    end
    lsu_req        =(next_lsu_req_flag)?0:lsu_req        ;
    lsu_ctrl_unsign=(next_lsu_req_flag)?0:lsu_ctrl_unsign;
    lsu_ctrl_load  =(next_lsu_req_flag)?0:lsu_ctrl_load  ;
    lsu_ctrl_store =(next_lsu_req_flag)?0:lsu_ctrl_store ;
    lsu_ctrl_word  =(next_lsu_req_flag)?0:lsu_ctrl_word  ;
    lsu_ctrl_half  =(next_lsu_req_flag)?0:lsu_ctrl_half  ;
    lsu_ctrl_byte  =(next_lsu_req_flag)?0:lsu_ctrl_byte  ;
    lsu_inst_id    =(next_lsu_req_flag)?0:lsu_inst_id    ;
    // lsu_flush      =(next_lsu_req_flag)?0:lsu_flush      ;
    lsu_flush_id   =(next_lsu_req_flag)?0:lsu_flush_id   ;
    // retire_vld     =(next_lsu_req_flag)?0:retire_vld     ;
    lsu_op1_val    =(next_lsu_req_flag)?0:lsu_op1_val    ;
    lsu_op2_val    =(next_lsu_req_flag)?0:lsu_op2_val    ;
    lsu_store_data =(next_lsu_req_flag)?0:lsu_store_data ;

    for (int i = 0; i < 100; i++) begin
        if (packets[i].packet_state==2) begin
            // count wait cycles
            if(packets[i].wait_cycle>200)begin
                 $display("INFO:packet%d didn't get dmem_req or ddr_req failed !",i);
                // $finish;
            end
            else begin
                packets[i].wait_cycle=packets[i].wait_cycle+1;
            end
        end
    end
    for (int i = 0; i < 100; i++) begin
        if (packets[i].cycle_remain==0 && packets[i].packet_state==0 && next_lsu_req_flag && !inst_id_in_lsu(i)) begin
            lsu_req        =1'b1                      ;
            lsu_ctrl_unsign=packets[i].lsu_ctrl_unsign;
            lsu_ctrl_load  =packets[i].lsu_ctrl_load  ;
            lsu_ctrl_store =packets[i].lsu_ctrl_store ;
            lsu_ctrl_word  =packets[i].lsu_ctrl_word  ;
            lsu_ctrl_half  =packets[i].lsu_ctrl_half  ;
            lsu_ctrl_byte  =packets[i].lsu_ctrl_byte  ;
            lsu_inst_id    =packets[i].lsu_inst_id    ;
            // lsu_flush      =                         ;
            lsu_flush_id   =packets[i].lsu_flush_id   ;
            // retire_vld     =packets[i].retire_vld     ;
            lsu_op1_val    =packets[i].lsu_op1_val    ;
            lsu_op2_val    =packets[i].lsu_op2_val    ;
            lsu_store_data =packets[i].lsu_store_data ;
            packets[i].packet_state=1;           //req sent
            send_ptr       =i;
            $display($time,"INFO:packet%d is sent",i);
            break;
        end
        if (packets[i].cycle_remain!=0 ) begin
            packets[i].cycle_remain=packets[i].cycle_remain-1;
        end
    end
endtask 

task detect_dmem_req();
    @(posedge clk);
        if(dmem_req==1) begin
            $display("INFO :dmem req detected !!");
             dmem_req_flag =0;
            for (int i = 0; i < 100; i++) begin
                if(packets[i].lsu_inst_id==dmem_req_tid && packets[i].packet_state==2) begin
                    packets[i].packet_state=3;
                    $display("packet%d get dmem req ",i);
                    dmem_req_flag=1;
                end
            end
            if (!dmem_req_flag) begin
                $display("INFO:detected illegal dmem_req !!" );
            end
        end
endtask

task detect_ddr_req();
    @(posedge clk)
        if(ddr_req==1)begin
            $display("INFO :ddr req detected !!");
             ddr_req_flag =0;
            for (int i = 0; i < 100; i++) begin
                if(packets[i].lsu_inst_id==ddr_req_tid && packets[i].packet_state==3) begin
                    packets[i].packet_state=4;
                    $display("packet%d get ddr req ",i);
                    ddr_req_flag=1;
                end
            end
            if (!ddr_req_flag) begin
                $display("INFO:detected illegal ddr_req !!" );
            end
        end
endtask

initial 
begin
    for (int  pac_index= 0; pac_index < 100; pac_index++) begin
        packets[pac_index]=new ();
        
        assert (packets[pac_index].randomize())
            $display("INFO:packet%d randomize succeed id:%d!",pac_index,packets[pac_index].lsu_inst_id);
        else
            $fatal("INFO:packet%d randomize failed !",pac_index);
    end
    test_flag      =0;
    lsu_req        =0;
    lsu_ctrl_unsign=0;
    lsu_ctrl_load  =0;
    lsu_ctrl_store =0;
    lsu_ctrl_word  =0;
    lsu_ctrl_half  =0;
    lsu_ctrl_byte  =0;
    lsu_inst_id    =0;
    lsu_flush      =0;
    lsu_flush_id   =0;
    retire_vld     =1;
    lsu_op1_val    =0;
    lsu_op2_val    =0;
    lsu_store_data =0;
    clk =0;
    rst_n =1;
    #1 rst_n =0;
    #3 rst_n =1;
    fork
        begin:send_packet_process
            while(1)begin
                send_packet;
            end
        end
        begin:get_dmem_req
            while(1)begin
                detect_dmem_req;
            end
        end
        begin:get_ddr_req
            while(1)begin
                detect_ddr_req;
            end
        end
        begin :get_dev_req 
            // 
        end
        begin : detect_resp
            // while(1)begin
            //     @(posedge lsu_resp_vld)

            // end

        end
        
    join
end

    frv_lsu_top udt
        (
            .clk              (clk),
            .rst_n            (rst_n),
            .pd_rst           (pd_rst),
            
            .lsu_req          (lsu_req),
            .lsu_ready        (lsu_ready),
            .lsu_ctrl_unsign  (lsu_ctrl_unsign),
            .lsu_ctrl_load    (lsu_ctrl_load),
            .lsu_ctrl_store   (lsu_ctrl_store),
            .lsu_ctrl_word    (lsu_ctrl_word),
            .lsu_ctrl_half    (lsu_ctrl_half),
            .lsu_ctrl_byte    (lsu_ctrl_byte),
            .lsu_inst_id      (lsu_inst_id),
            .lsu_flush        (lsu_flush),
            .lsu_flush_id     (lsu_flush_id),
            .retire_vld       (retire_vld),
            .lsu_op1_val      (lsu_op1_val),
            .lsu_op2_val      (lsu_op2_val),
            .lsu_store_data   (lsu_store_data),
            
            .lsu_resp_inst_id (lsu_resp_inst_id),
            .lsu_resp_vld     (lsu_resp_vld),
            .lsu_resp_rd_vld  (lsu_resp_rd_vld),
            .lsu_resp_rd_ind  (lsu_resp_rd_ind),
            .lsu_resp_rd_val  (lsu_resp_rd_val),
            .lsu_exp_vld      (lsu_exp_vld),

            .dmem_req         (dmem_req),
            .dmem_type       (dmem_type),
            .dmem_req_tid     (dmem_req_tid),
            .dmem_addr        (dmem_addr),
            .dmem_wdata       (dmem_wdata),
            .dmem_rdata       (dmem_rdata),
            .dmem_ack         (dmem_ack),
            .dmem_hit         (dmem_hit),
            .dmem_ack_type    (dmem_ack_type),
            .dmem_flush       (dmem_flush),
            
            .ddr_req          (ddr_req),
            .ddr_type         (ddr_type),
            .ddr_req_tid      (ddr_req_tid),
            .ddr_addr         (ddr_addr),
            .ddr_ack          (ddr_ack),
            .ddr_ack_tid      (ddr_ack_tid),
            .ddr_ack_type    (ddr_ack_type),
            .ddr_wdata        (ddr_wdata),
            .ddr_rdata        (ddr_rdata),

            .dev_awtid        (dev_awtid),
            .dev_awaddr       (dev_awaddr),
            .dev_awprot       (dev_awprot),
            .dev_awvalid      (dev_awvalid),
            .dev_awready      (dev_awready),
            .dev_wtid         (dev_wtid),
            .dev_wdata        (dev_wdata),
            .dev_wstrb        (dev_wstrb),
            .dev_wvalid       (dev_wvalid),
            .dev_wready       (dev_wready),
            .dev_btid         (dev_btid),
            .dev_bresp        (dev_bresp),
            .dev_bvalid       (dev_bvalid),
            .dev_bready       (dev_bready),
            .dev_artid        (dev_artid),
            .dev_araddr       (dev_araddr),
            .dev_arprot       (dev_arprot),
            .dev_arvalid      (dev_arvalid),
            .dev_arready      (dev_arready),
            .dev_rtid         (dev_rtid),
            .dev_rdata        (dev_rdata),
            .dev_rresp        (dev_rresp),
            .dev_rvalid       (dev_rvalid),
            .dev_rready       (dev_rready)
        );


    cache_verify inst_cache_verify
        (
            .clk          (clk),
            .rst_n        (rst_n),
            .dmem_req     (dmem_req),
            .dmem_type    (dmem_type),
            .dmem_req_tid (dmem_req_tid),
            .dmem_addr    (dmem_addr),
            .dmem_wdata   (dmem_wdata),
            .dmem_rdata   (dmem_rdata),
            .dmem_ack     (dmem_ack),
            .dmem_hit     (dmem_hit),
            .dmem_ack_type(dmem_ack_type),
            .dmem_flush   (dmem_flush)
        );

        dram_inf_verify inst_dram_inf_verify
        (
            .clk          (clk),
            .rst_n        (rst_n),
            .ddr_req      (ddr_req),
            .ddr_type     (ddr_type),
            .ddr_req_tid  (ddr_req_tid),
            .ddr_addr     (ddr_addr),
            .ddr_wdata    (ddr_wdata),
            .ddr_rdata    (ddr_rdata),
            .ddr_ack      (ddr_ack),
            .ddr_ack_type (ddr_ack_type),
            .ddr_ack_tid  (ddr_ack_tid)
        );




endmodule